﻿using Grasshopper;
using Grasshopper.GUI;
using Grasshopper.GUI.Canvas;
using Grasshopper.Kernel;
using Rhino.Geometry;
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using Rhino.DocObjects.Tables;
using Rhino.Display;


// In order to load the result of this wizard, you will also need to
// add the output bin/ folder of this project to the list of loaded
// folder in Grasshopper.
// You can use the _GrasshopperDeveloperSettings Rhino command for that.

namespace YangTools
{
    public class RhinoViewData
    {
        public RhinoView CurrentView { get; set; }

        public string Title { get; set; }

        public IntPtr Handle { get { return CurrentView.Handle; } }

        public IntPtr Parent { get { return YangToolsTopViewport.GetParent(CurrentView.Handle); } }
        public RhinoViewData(RhinoView rv)
        {
            CurrentView = rv;
            Title = "Yang";
        }

    }

    //gha插件中的一个电池
    public class YangToolsTopViewport : GH_Component
    {
        /// <summary>
        /// 电池图标
        /// </summary>
        protected override System.Drawing.Bitmap Icon
        {
            get
            {
                //为电池创建图标
                return Properties.Resources.TopMostViewport;
            }
        }

        /// <summary>
        /// 电池GUID，每个组件都必须具有唯一的Guid才能识别它
        /// </summary>
        /// <remarks>此Guid不要更改，否则使用旧ID的旧ghx文件将在加载过程中部分失败</remarks>
        public override Guid ComponentGuid
        {
            get { return new Guid("daf95ca9-7735-4d8c-8f43-b16059d4c5c4"); }
        }

        public static List<RhinoViewData> ViewportWindows = new List<RhinoViewData>();

        public static int locationOffsetX = 0;
        public static int locationOffsetY = 0;

        /// <summary>
        /// 构造方法
        /// Each implementation of GH_Component must provide a public 
        /// constructor without any arguments.
        /// gh电池的每一个实例都要提供一个没有参数的构造函数
        /// Category represents the Tab in which the component will appear, 
        /// Category 表示将在其中显示组件的选项卡
        /// Subcategory the panel. If you use non-existing tab or panel names, 
        /// new tabs/panels will automatically be created.
        /// Subcategory子类别面板，如果不存在将自动创建新的标签页
        /// </summary>
        public YangToolsTopViewport()
          : base("TopViewport", "TV",
              "创建浮动窗口",
              "YangTools", "Viewport")//使用父类（GH_Component）的构造方法
        {
            // 当画布生成时添加按钮
            Instances.CanvasCreated += AddToolstrip;

            // 当关闭 RhinoView 时的方法
            RhinoView.Destroy += RhinoView_Destroy;
        }

        /// <summary>
        /// Toolstrip 上在添加一个按钮
        /// </summary>
        /// <param name="canvas"></param>
        public void AddToolstrip(GH_Canvas canvas)
        {
            GH_DocumentEditor de = Instances.DocumentEditor;
            var con = ((Control)de).Controls;
            var panel = ((Control.ControlCollection)con)[0] as Panel;
            var pCon = ((Control)panel).Controls;
            GH_Toolstrip toolStrip = ((Control.ControlCollection)pCon)[1] as GH_Toolstrip;

            foreach (ToolStripItem item in toolStrip.Items)
            {
                // 如果已经创建了按钮，就不再创建
                // 由于每次打开gh都会重新生成实例，所以不能使用一个变量记录是否已经创建过了，只能直接判断是否存在一个这样的按钮
                try
                {
                    var itemTag = (Guid)item.Tag;
                    if (itemTag == ComponentGuid) return;
                }
                catch
                {
                }
            }

            var toolStripItem = toolStrip.Items.Add("", Icon, CreatViewport);
            toolStripItem.Tag = ComponentGuid;  // 使用插件自身的Guid作为标记，避免多次添加按钮
            toolStripItem.TextImageRelation = TextImageRelation.Overlay;
        }

        /// <summary>
        /// 创建一个浮动视窗
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void CreatViewport(object sender, EventArgs e)
        {
            ViewTable vt = Rhino.RhinoDoc.ActiveDoc.Views;

            var documentLocation = Instances.DocumentEditor.Location; // gh的位置
            var documentSize = Instances.DocumentEditor.Size; // gh的尺寸
            var viewportSize = Instances.ActiveCanvas.Viewport.Size; // 当前Canvas的尺寸

            int height = 500; // 创建视窗的高度为500
            double proportion = (double)(vt.ActiveView.Size.Width) / (double)(vt.ActiveView.Size.Height);
            int width = (int)(proportion * height); // 以当前活动窗口的比例确定宽度
            int X = documentLocation.X + viewportSize.Width - locationOffsetX - width;
            int Y = documentLocation.Y + documentSize.Height - viewportSize.Height + locationOffsetY;

            // 创建一个浮动犀牛视窗
            RhinoView view = vt.Add("Yang", DefinedViewportProjection.Perspective, new Rectangle(X, Y, width, height), true);
            view.ActiveViewport.Name = "Perspective";


            RhinoViewData viewData = new RhinoViewData(view);

            // 1、将窗口添加到 ViewportWindows 中作为记录
            ViewportWindows.Add(viewData);

            // 2、将窗口置顶
            SetWindowPos(viewData.Parent, -1, 0, 0, 0, 0, 3); // 设置窗口置顶

            // 3、将所有窗口的标题修改为设置的值
            // 每次创建新的浮动视窗，以前的视窗的title都会还原，所以要对每一个都修改
            foreach (var item in ViewportWindows)
            {
                SetWindowText(item.Parent, item.Title);
            }

            // 4、修改 窗口的位置偏移
            // 保证每次创建窗口不重叠且不超出屏幕，偏移每次创建时的 X 和 Y
            locationOffsetX += 20;
            locationOffsetY += 20;
            if (Screen.PrimaryScreen.WorkingArea.Height - locationOffsetX < width) locationOffsetX = 0;
            if (locationOffsetY > Screen.PrimaryScreen.WorkingArea.Height - height) locationOffsetY = 0;
        }

        /// <summary>
        /// 窗口关闭时的事件：从ViewportWindows中删除窗口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RhinoView_Destroy(object sender, ViewEventArgs e)
        {
            foreach (var item in ViewportWindows)
            {
                if (item.CurrentView == e.View)
                {
                    ViewportWindows.Remove(item);
                    return;
                }

            }

        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        internal static extern IntPtr GetParent(IntPtr hWnd);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        internal static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int Width, int Height, int flags);

        [DllImport("user32.dll", EntryPoint = "SetWindowText")]
        public static extern int SetWindowText(IntPtr hwnd, string lpString);

        /// <summary>
        /// 设置输入端
        /// Registers all the input parameters for this component.
        /// </summary>
        protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
        {
            //                          全称    简称    描述简介    单个输入，还可以是list、tree
            pManager.AddBooleanParameter("ShowIndex", "si", "显示每个窗口的标题", GH_ParamAccess.item, false);
            pManager.AddIntegerParameter("Index", "i", "从 0 开始的窗口索引，默认为0（第一个）", GH_ParamAccess.item, 0);
            pManager.AddTextParameter("Title", "t", "标题名称", GH_ParamAccess.item, "Yang");
        }

        /// <summary>
        /// 设置输出端
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
        {
        }

        /// <summary>
        /// 该电池的主要代码 This is the method that actually does the work.
        /// </summary>
        /// <param name="DA">The DA object can be used to retrieve data from input parameters and 
        /// to store data in output parameters.
        /// DA用于获取输入参数</param>
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            //获取输入参数
            int index = 0;
            string title = "Yang";
            bool showIndex = false;
            DA.GetData("Index", ref index);
            DA.GetData("Title", ref title);
            DA.GetData("ShowIndex", ref showIndex);

            if (ViewportWindows.Count == 0)
            {
                // 没有创建过浮动视窗就添加了这个电池，那就创建一个

                CreatViewport(this, new EventArgs());
            }

            if (showIndex)
            {
                for (int i = 0; i < ViewportWindows.Count; i++)
                {
                    ViewportWindows[i].Title += " - " + i.ToString();
                    SetWindowText(ViewportWindows[i].Parent, ViewportWindows[i].Title);
                }
            }

            // 显示视窗索引
            if (index > ViewportWindows.Count)
            {
                this.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "输入的 Index 超出浮动视窗的个数");
                return;
            }

            // 修改窗口标题
            SetWindowText(ViewportWindows[index].Parent, title);
        }
    }
}
